package com.example.demo.config;

import com.zaxxer.hikari.HikariDataSource;
import lombok.SneakyThrows;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.TransactionIsolationLevel;
import org.apache.ibatis.session.defaults.DefaultSqlSession;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Configuration
public class MultiDataSourceConfig {
    //    @Bean
    SqlSession sqlSession (DataSource dataSource) {
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        Environment environment = new Environment(dataSource.toString(), new SpringManagedTransactionFactory(), dataSource);

        configuration.setEnvironment(environment);
//        DefaultSqlSessionFactory defaultSqlSessionFactory = new DefaultSqlSessionFactory(configuration);
        final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
        Transaction tx = transactionFactory.newTransaction(environment.getDataSource(), TransactionIsolationLevel.NONE, true);
        final Executor executor = configuration.newExecutor(tx, ExecutorType.SIMPLE);
        return new DefaultSqlSession(configuration, executor, true);
//        return defaultSqlSessionFactory.openSession();
    }

/*    @Bean
    SqlSessionFactory sqlSessionFactory (DataSource dataSource) throws Exception {
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        Environment environment = new Environment(dataSource.toString(), new SpringManagedTransactionFactory(), dataSource);
        configuration.setEnvironment(environment);
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setConfiguration(configuration);
//        Resource[] resources = new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml");
//        sqlSessionFactoryBean.setMapperLocations(resources);
        return sqlSessionFactoryBean.getObject();
    }*/

    //    @Bean
    SqlSessionFactory sqlSessionFactory (DataSource dataSource) {
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        Environment environment = new Environment(dataSource.toString(), new SpringManagedTransactionFactory(), dataSource);
        configuration.setEnvironment(environment);
        return new DefaultSqlSessionFactory(configuration);
    }

    private TransactionFactory getTransactionFactoryFromEnvironment (Environment environment) {
        if (environment == null || environment.getTransactionFactory() == null) {
            return new ManagedTransactionFactory();
        }
        return environment.getTransactionFactory();
    }

    @Bean
    public MultiDataSource multiDataSource (DataSourceProperties properties, MultiDataSourceProperties dataSources) {
        MultiDataSource multiDataSource = new MultiDataSource();
        HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class);
        if (StringUtils.hasText(properties.getName())) {
            dataSource.setPoolName(properties.getName());
        }
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("master", dataSource);
        multiDataSource.putDataSource("master", dataSource);
        for (HikariDataSource source : dataSources.getDataSources()) {
            multiDataSource.putDataSource("slave_" + source.getPoolName(), source);
            targetDataSources.put("slave_" + source.getPoolName(), source);
        }
        multiDataSource.setTargetDataSources(targetDataSources);
        multiDataSource.setDefaultTargetDataSource(dataSource);
        return multiDataSource;
    }

    protected static <T> T createDataSource (DataSourceProperties properties, Class<? extends DataSource> type) {
        return (T) properties.initializeDataSourceBuilder().type(type).build();
    }

}
